package forum.client.ui;

import java.awt.Color;
import java.awt.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import forum.client.controllerlayer.ControllerHandler;
import forum.client.controllerlayer.ControllerHandlerFactory;
import forum.client.ui.events.ForumTreeErrorEvent;
import java.util.concurrent.ExecutionException;

/**
 * @author Tomer Heber
 *
 */
public class ForumTree implements ForumTreeHandler {

	/**
	 * The JTree GUI component.
	 */
	private JTree m_tree;

	/**
	 * The JPanel GUI component.
	 */
	private JPanel m_panel;

	/**
	 * A pipe interface to communicate with the controller layer.
	 */
	private ControllerHandler m_pipe;

	/**
	 * A thread pool that is used to initiate operations in the controller layer.
	 */
	private ExecutorService m_pool = Executors.newCachedThreadPool();

        public static UserType userType = UserType.GUEST;
        public static String userName;

	public ForumTree() {
		UIManager.put("Tree.collapsedIcon", new ImageIcon("./images/plus-8.png"));
		UIManager.put("Tree.expandedIcon", new ImageIcon("./images/minus-8.png"));

		m_pipe = ControllerHandlerFactory.getPipe();
		/* Add an observer to the controller (The observable). */
		m_pipe.addObserver(new ForumTreeObserver(this));

		m_tree = new JTree();
		m_tree.putClientProperty("JTree.lineStyle", "None");

		refreshForum(m_pipe.getForumView()[1]);

		ForumTreeCellRenderer renderer = new ForumTreeCellRenderer(this);
		m_tree.setCellRenderer(renderer);
		m_tree.setCellEditor(new ForumTreeCellEditor(renderer));
		m_tree.setEditable(true);

		m_tree.addTreeSelectionListener(new TreeSelectionListener() {

			@Override
			public void valueChanged(TreeSelectionEvent e) {
				BasicTreeUI ui = (BasicTreeUI) m_tree.getUI();
				ui.setLeftChildIndent(ui.getLeftChildIndent());
				ui.setRightChildIndent(ui.getRightChildIndent());
			}
		});

		m_panel = new JPanel();
		m_panel.setBackground(Color.WHITE);
//		JScrollPane pane = new JScrollPane(m_tree);
//		pane.setPreferredSize(new Dimension(610,435));
		m_panel.add(m_tree);
		m_panel.setAlignmentX(Component.LEFT_ALIGNMENT);

		//m_panel.setPreferredSize(new Dimension(620,460));
	}

	/**
	 *
	 * @return The forum tree GUI component.
	 */
	public Component getForumTreeUI() {
		return m_panel;
	}
	
	@Override
	public synchronized void refreshForum(String encodedView) {
		ForumCell rootCell = decodeView(encodedView);
		DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(rootCell);

		Stack<DefaultMutableTreeNode> stack = new Stack<DefaultMutableTreeNode>();
		stack.add(rootNode);

		while (!stack.isEmpty()) {
			DefaultMutableTreeNode node = stack.pop();
			ForumCell cell = (ForumCell)(node.getUserObject());
			for (ForumCell sonCell : cell.getSons()) {
				DefaultMutableTreeNode sonNode = new DefaultMutableTreeNode(sonCell);
				node.add(sonNode);
				stack.add(sonNode);
			}
		}

		DefaultTreeModel model = new DefaultTreeModel(rootNode);
		m_tree.setModel(model);
		for (int i = 0; i < m_tree.getRowCount(); i++) {
	         m_tree.expandRow(i);
		}

	}

	@Override
	public synchronized void notifyError(String errorMessage) {
		JFrame frame = new JFrame();
		JOptionPane.showMessageDialog(frame,
			    errorMessage,
			    "Operation failed.",
			    JOptionPane.WARNING_MESSAGE);
	}

        public synchronized void notifyError(String errorMessage, Component c) {
		JOptionPane.showMessageDialog(c,
			    errorMessage,
			    "Operation failed.",
			    JOptionPane.WARNING_MESSAGE);
	}

        public JTree getTree(){
            return this.m_tree;
        }
        
	/**
	 * Receives an encoding describing the forum tree.<br>
	 * It decodes the description and returns the tree representation
     * in a ForumCell instance.
	 *
	 * @return The tree representing the forum.
	 */
	private ForumCell decodeView(String encodedView) {

		StringTokenizer str_tok = new StringTokenizer(encodedView," \t\n\r\f",true);
		Map<Integer, ForumCell> map = new HashMap<Integer, ForumCell>();
		Map<ForumCell, Vector<Integer>> cellToReplies = new HashMap<ForumCell, Vector<Integer>>();
		Vector<Integer> allReplies = new Vector<Integer>();
		while (str_tok.hasMoreTokens()) {
			Integer id =  Integer.parseInt(str_tok.nextToken());
			str_tok.nextToken();
			String user = str_tok.nextToken();
			str_tok.nextToken();
			String content = null;
			Vector<Integer> replies = null;
			try {
				content = parseContent(str_tok);
				replies = getReplies(str_tok);
			} catch (Exception e) {
				System.err.println(e.getMessage());
				System.exit(-1);
			}
			str_tok.nextToken(); //to read /n
			allReplies.addAll(replies);
			ForumCell current = new ForumCell(id, user, content);
			map.put(id, current);
			cellToReplies.put(current,replies);
		}
		for (ForumCell f: cellToReplies.keySet()) {
			for (Integer i: cellToReplies.get(f)) {
				f.add(map.get(i));
			}
		}

		ForumCell root = new ForumCell(-2,"TheForumRoot","Click To Open");
		/*for (Integer i: map.keySet()) {
			if (!allReplies.contains(i)) {
				root.add(map.get(i));
			}
		}*/
                Integer[] arr = map.keySet().toArray(new Integer[0]);
                for (int i = arr.length-1; i>=0; i--){
                    Integer temp = arr[i];
                    if (!allReplies.contains(temp)) {
                        root.add(map.get(temp));
                    }
                }

		return root;

	}


	private Vector<Integer> getReplies(StringTokenizer strTok) throws Exception {
		if (!strTok.nextToken().equals("<replies>")) {
			throw new Exception("invalid replies token, when trying to parse forum encoding.");
		}
		strTok.nextToken();
		String token=strTok.nextToken();
		Vector<Integer> ans = new Vector<Integer>();
		while (!token.equals("</replies>")) {
			ans.add(Integer.parseInt(token));
			strTok.nextToken();
			token = strTok.nextToken();
		}
		return ans;
	}

	private String parseContent(StringTokenizer strTok) throws Exception {
		if (!strTok.nextToken().equals("<content>")) {
			throw new Exception("invalid content token, when trying to parse forum encoding.");
		}
		strTok.nextToken();
		String ans = "";
		String token=strTok.nextToken();
		while (!token.equals("</content>")) {
			ans+=token;
			token=strTok.nextToken();
		}
		strTok.nextToken();
		return ans;
	}

	/**
	 * Modifies a message, and updates the forum accordingly.
	 *
	 * @param newContent The new content of the message.
	 */
	public void modifyMessage(final String newContent, final JButton button) {
		//button.setEnabled(false);
                SwingWorker<String[], Void> modifyWorker = new SwingWorker<String[], Void>() {
                    public String[] doInBackground() {
                            DefaultMutableTreeNode node = (DefaultMutableTreeNode)m_tree.getSelectionPath().getLastPathComponent();
                            ForumCell cell = (ForumCell) node.getUserObject();
                            return m_pipe.modifyMessage(cell.getId(),newContent,button);

                    }
		};
		m_pool.execute(modifyWorker);

                try {
                    String[] ans = (String[])modifyWorker.get();
                    if (ans[0].equals("succ")){
                        m_pipe.refreshForum(m_pool);
                    }
                    else{
                        m_pipe.notifyObservers(new ForumTreeErrorEvent(ans[1]));
                    }
                } catch (InterruptedException ex) {}
                catch (ExecutionException ex) {
                    m_pipe.notifyObservers(new ForumTreeErrorEvent("Execution exception."));
                }
                
	}

	/**
	 * Replies to the selected message.
	 */
	public void replyToMessage(final JButton button) {
		//button.setEnabled(false);
        DefaultMutableTreeNode node = (DefaultMutableTreeNode)m_tree.getSelectionPath().getLastPathComponent();
		ForumCell cell = (ForumCell) node.getUserObject();
        //new ReplyToMessage((int)cell.getId()).setVisible(true);
		new ReplyToMessage((int)cell.getId(),button).setVisible(true);
	}

	/**
	 * Deletes the selected message.
	 */
	public void deleteMessage(final JButton button) {
		//button.setEnabled(false);
                SwingWorker<String[], Void> deleteWorker = new SwingWorker<String[], Void>() {
                    public String[] doInBackground() {
				DefaultMutableTreeNode node = (DefaultMutableTreeNode)m_tree.getSelectionPath().getLastPathComponent();
				ForumCell cell = (ForumCell) node.getUserObject();
				return m_pipe.deleteMessage(cell.getId(),button);
			}
		};
		m_pool.execute(deleteWorker);
                try {
                    String[] ans = (String[])deleteWorker.get();
                    if (ans[0].equals("succ")){
                        m_pipe.refreshForum(m_pool);
                    }
                    else{
                        m_pipe.notifyObservers(new ForumTreeErrorEvent(ans[1]));
                    }
                } catch (InterruptedException ex) {}
                catch (ExecutionException ex) {
                    m_pipe.notifyObservers(new ForumTreeErrorEvent("Execution exception."));
                }

	}

	/**
	 * Adds a new message.
	 */
	public void addNewMessage(final JButton button) {
	/*	button.setEnabled(false);
		m_pool.execute(new SwingWorker<Void, Void>() {
                        public Void doInBackground() {
				m_pipe.addNewMessage(button);
				return null;
			}
		});*/
	}


}
